package cn.com.anysdk.oss.impl;

import cn.com.anysdk.oss.api.IOssService;
import cn.com.anysdk.oss.api.IOssConfig;
import cn.com.anysdk.oss.api.UploadOptions;
import cn.com.anysdk.oss.config.MinioConfig;
import cn.com.anysdk.oss.exception.OssException;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.DeleteObject;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
public class MinioOssService implements IOssService {
    private final MinioClient minioClient;
    private final MinioConfig minioConfig;
    private final IOssConfig config;

    public MinioOssService(IOssConfig config) {
        if (!(config instanceof MinioConfig)) {
            throw new IllegalArgumentException("Config must be instance of MinioConfig");
        }
        this.minioConfig = (MinioConfig) config;
        this.minioClient = this.minioConfig.createMinioClient();
        this.config = config;
    }

    @Override
    public String uploadFile(String objectKey, File file) {
        try {
            minioClient.uploadObject(
                    UploadObjectArgs.builder()
                            .bucket(minioConfig.getBucketName())
                            .object(objectKey)
                            .filename(file.getAbsolutePath())
                            .build()
            );
            return getFileUrl(objectKey);
        } catch (Exception e) {
            throw new OssException("Failed to upload file: " + e.getMessage(), e);
        }
    }

    @Override
    public String uploadFile(String objectKey, InputStream inputStream, long size) {
        try {
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(minioConfig.getBucketName())
                    .object(objectKey)
                    .stream(inputStream, size, -1)
                    .build());
            log.debug("Upload file success, path: {}", objectKey);
            return getFileUrl(objectKey);
        } catch (Exception e) {
            throw new OssException("Upload file failed: " + e.getMessage(), e);
        }
    }

    @Override
    public String uploadFile(String objectKey, File file, UploadOptions options) {
        try {
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(minioConfig.getBucketName())
                    .object(objectKey)
                    .stream(new FileInputStream(file), file.length(), -1)
                    .contentType(options.getContentType())
                    .build());
            log.debug("Upload file success, path: {}", objectKey);
            return getFileUrl(objectKey, options.isPrivate() ? options.getExpireTime() : null);
        } catch (Exception e) {
            throw new OssException("Upload file failed: " + e.getMessage(), e);
        }
    }

    @Override
    public InputStream downloadFile(String objectKey) {
        try {
            return minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(minioConfig.getBucketName())
                            .object(objectKey)
                            .build()
            );
        } catch (Exception e) {
            throw new OssException("Failed to download file: " + e.getMessage(), e);
        }
    }

    @Override
    public void deleteFile(String objectKey) {
        try {
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(minioConfig.getBucketName())
                            .object(objectKey)
                            .build()
            );
        } catch (Exception e) {
            throw new OssException("Failed to delete file: " + e.getMessage(), e);
        }
    }

    @Override
    public void deleteFiles(List<String> objectKeys) {
        try {
            List<DeleteObject> objects = objectKeys.stream()
                    .map(DeleteObject::new)
                    .toList();
            minioClient.removeObjects(RemoveObjectsArgs.builder()
                    .bucket(minioConfig.getBucketName())
                    .objects(objects)
                    .build());
        } catch (Exception e) {
            throw new OssException("Delete files failed: " + e.getMessage(), e);
        }
    }

    @Override
    public String getFileUrl(String objectKey) {
        try {
            String url = minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .method(Method.GET)
                            .bucket(minioConfig.getBucketName())
                            .object(objectKey)
                            .expiry(7, TimeUnit.DAYS)
                            .build()
            );
            return minioConfig.getCustomDomain() != null ?
                   url.replace(minioConfig.getEndpoint(), minioConfig.getCustomDomain()) :
                   url;
        } catch (Exception e) {
            throw new OssException("Failed to get file URL: " + e.getMessage(), e);
        }
    }

    @Override
    public String getSignedUrl(String objectKey, long expireSeconds) {
        return getFileUrl(objectKey, expireSeconds);
    }

    @Override
    public boolean doesObjectExist(String objectKey) {
        try {
            minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(minioConfig.getBucketName())
                            .object(objectKey)
                            .build()
            );
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public IOssConfig getConfig() {
        return this.config;
    }

    @Override
    public boolean validateConfig() {
        return config != null &&
               config.getAccessKeyId() != null && !config.getAccessKeyId().trim().isEmpty() &&
               config.getAccessKeySecret() != null && !config.getAccessKeySecret().trim().isEmpty() &&
               config.getEndpoint() != null && !config.getEndpoint().trim().isEmpty() &&
               config.getBucketName() != null && !config.getBucketName().trim().isEmpty();
    }

    private String getFileUrl(String objectKey, Long expireTime) {
        try {
            if (minioConfig.isPrivateMode() || expireTime != null) {
                String url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                        .bucket(minioConfig.getBucketName())
                        .object(objectKey)
                        .expiry(expireTime != null ? expireTime.intValue() : 3600, TimeUnit.SECONDS)
                        .build());
                return url;
            } else {
                return String.format("http://%s/%s/%s", minioConfig.getEndpoint(), minioConfig.getBucketName(), objectKey);
            }
        } catch (Exception e) {
            throw new OssException("Generate URL failed: " + e.getMessage(), e);
        }
    }
}